﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DeSleeper.Library;
using MbUnit.Framework;

namespace DeSleeper.Tests
{
    [TestFixture]
    public class SelfFlatteningExceptionFixture
    {
        [Test]
        public void OneLevel()
        {
            try
            {
                selfLevelOne();
                Assert.Fail();
            }
            catch (Exception e)
            {
                Assert.AreEqual("Processing failed for Object. Something went wrong.", e.Message);
                Assert.FullMatch(e.ToString(),
                                 @"System.InvalidOperationException: Processing failed for Object. Something went wrong.\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfLevelOne\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.OneLevel\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*");
            }
        }

        [Test]
        public void TwoLevel()
        {
            try
            {
                selfLevelTwo();
                Assert.Fail();
            }
            catch (Exception e)
            {
                Assert.AreEqual("Processing failed for Second Level Object. Processing failed for Object. Something went wrong.", e.Message);
                Assert.FullMatch(e.ToString(),
                                 @"System.InvalidOperationException: Processing failed for Second Level Object. Processing failed for Object. Something went wrong.\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfLevelOne\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfLevelTwo\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.TwoLevel\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*");
            }
        }

        [Test]
        public void NestedOneLevel()
        {
            try
            {
                selfNestedLevelOne();
                Assert.Fail();
            }
            catch (Exception e)
            {
                Assert.AreEqual("Processing failed for Object. Normally nested exception.", e.Message);
                Assert.FullMatch(e.ToString(),
                                 @"System.InvalidOperationException: Processing failed for Object. Normally nested exception. ---> System.InvalidOperationException: Something went wrong.\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" --- End of inner exception stack trace ---\w*?\s*?" + 
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfNestedLevelOne\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.NestedOneLevel\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*");
            }
        }

        [Test]
        public void NestedTwoLevel()
        {
            try
            {
                selfNestedLevelTwo();
                Assert.Fail();
            }
            catch (Exception e)
            {
                Assert.AreEqual("Processing failed for Second Level Object. Processing failed for Object. Normally nested exception.", e.Message);
                Assert.FullMatch(e.ToString(),
                                 @"System.InvalidOperationException: Processing failed for Second Level Object. Processing failed for Object. Normally nested exception. ---> System.InvalidOperationException: Something went wrong.\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.exceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" --- End of inner exception stack trace ---\w*?\s*?" + 
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionGenerator\(\) in [ \w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.nestedExceptionWithStackGenerator\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfNestedLevelOne\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.selfNestedLevelTwo\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*?\s*?" +
                                 @" at DeSleeper.Tests.SelfFlatteningExceptionFixture.NestedTwoLevel\(\) in [\w\s.:\\]*?SelfFlatteningExceptionFixture.cs:line \w*");
            }
        }

        private void selfLevelTwo()
        {
            try
            {
                selfLevelOne();
            }
            catch (Exception e)
            {
                throw new SelfFlatteningException("Processing failed for Second Level Object.", e);
            }
        }

        private void selfLevelOne()
        {
            try
            {
                exceptionWithStackGenerator();
            }
            catch (Exception e)
            {
                throw new SelfFlatteningException("Processing failed for Object.", e);
            }
        }

        private void exceptionWithStackGenerator()
        {
            exceptionGenerator();
        }

        private void exceptionGenerator()
        {
            throw new InvalidOperationException("Something went wrong.");
        }

        private void selfNestedLevelTwo()
        {
            try
            {
                selfNestedLevelOne();
            }
            catch (Exception e)
            {
                throw new SelfFlatteningException("Processing failed for Second Level Object.", e);
            }
        }

        private void selfNestedLevelOne()
        {
            try
            {
                nestedExceptionWithStackGenerator();
            }
            catch (Exception e)
            {
                throw new SelfFlatteningException("Processing failed for Object.", e);
            }
        }

        private void nestedExceptionWithStackGenerator()
        {
            nestedExceptionGenerator();
        }

        private void nestedExceptionGenerator()
        {
            try
            {
                exceptionWithStackGenerator();
            }
            catch (Exception exception)
            {
                throw new InvalidOperationException("Normally nested exception.", exception);
            }
        }
    }
}
